package cn.zswltech.gruul.auth;

import cn.hutool.core.util.StrUtil;
import cn.zswltech.gruul.biz.service.GroupOaService;
import cn.zswltech.gruul.biz.service.LoginService;
import cn.zswltech.gruul.common.constant.SystemConstant;
import cn.zswltech.gruul.common.entity.UserDO;
import cn.zswltech.gruul.common.result.MSG;
import cn.zswltech.gruul.common.result.Response;
import cn.zswltech.gruul.domain.entity.CheckResult;
import cn.zswltech.gruul.web.api.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static cn.zswltech.gruul.web.api.util.RequestUtil.getIpAddr;

/**
 * @author Jim
 * @version 1.0.0
 * @descripition: 内部应用校验
 * @date 2024/2/2 10:38
 */
@Service
@Slf4j
public class WLApplicationChecker extends AbstractAuthChecker{
    
    @Resource
    private GroupOaService groupOaService;
    @Value("#{'${mithras.login.whitelist:}'.split(',')}")
    private Set<String> whiteList = new HashSet<>();

    @Value("${gruul.system.intercept.pre:}")
    private String pre;

    @Value("${gruul.system.intercept.open:}")
    private Boolean open;

    @Value("${gruul.system.intercept.csrf.open:}")
    private Boolean csrf;

    @Resource
    private CommonFunctionCheck commonFunctionCheck;



    /**
     * 功能白名单，不需要注册
     */
    private static List<String> systemPath = new ArrayList<String>();

    static {

        // 退出登录
        systemPath.add("/user/logout");
        // 根据clientID获取token
        systemPath.add("/user/getToken");
        // 获取用户信息
        systemPath.add("/user/checkUser");
        // 获取菜单功能权限
        systemPath.add("/user/menuTree");
        // 用户个人中心相关接口
        systemPath.add("/userCenter*");
        // 参数配置
        systemPath.add("/system/config/value/*");
        // 获取机构列表
        systemPath.add("/org");
        // 获取 观远BI看板SSO接口
        systemPath.add("/auth/getBISSOToken");
    }

    @Resource(name = "loginService")
    private LoginService loginService;
    
    @Override
    public Boolean check(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String wlToken = CookieUtils.getWlToken(request);
        if (StrUtil.isEmpty(wlToken) && whiteList.contains(getIpAddr(request))) {
            WhiteListUtil.setWhiteListFlagHolder(true);
            return true;
        }
        if(groupOaService.checkTocken(request)){
            return true;
        }

        // 访问路径
        String servletPath = pre + request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }


        String method = request.getMethod();
        if (StringUtils.isEmpty(wlToken)) {
            log.info("wlToken验证失败");
            print(response, Response.error(MSG.req_error_sign_not_exists), HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        // CSRF校验
        if (csrf == null){csrf = false;}
        if (csrf && !TokenUtil.validateCSRFToken(request)) {
            print(response, Response.error(MSG.req_error_permission_csrf_error), HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        //验证JWT的签名，返回CheckResult对象
        CheckResult checkResult = JwtUtils.validateJWT(wlToken);
        if (!checkResult.isSuccess()) {
            switch (checkResult.getErrCode()) {
                // 签名验证不通过
                case SystemConstant.JWT_ERRCODE_FAIL:
                    log.info("签名验证不通过");
                    print(response, Response.error(MSG.req_error_sign_not_pass), HttpStatus.UNAUTHORIZED.value());
                    break;
                // 签名过期，返回过期提示码
                case SystemConstant.JWT_ERRCODE_EXPIRE:
                    log.info("签名过期");
                    print(response, Response.error(MSG.req_error_sign_expire), HttpStatus.UNAUTHORIZED.value());
                    break;
                default:
                    break;
            }
            return false;
        }
        // 预留 判断账号是否拥有servletPath的权限
        // 账号
        String account = (String) checkResult.getClaims().get("sub");
        //pwd
        String pwd = (String) checkResult.getClaims().get("jti");
        // 检查用户是否存在,唯一登录校验
        UserDO userDO = new UserDO();
        userDO.setAccount(account);
        userDO.setPwd(pwd);
//        userDO.setTokenMD5(MD5Util.MD5(wlToken));
        Response res = loginService.checkUser(userDO, response);
        if (!res.isSuccess()) {
            int httpCode = HttpStatus.UNAUTHORIZED.value();
            // 返回4011003，前端跳转到退出提示页面
//            if (res.getCode() == MSG.req_error_auth_multiple_login.code()) {
//                httpCode = HttpStatus.UNAUTHORIZED.value();
//            }
            print(response, res, httpCode);
            return false;
        }

        // 校验用户是否有指定菜单或功能的权限
        return checkAccountHasPathPermission(account, servletPath, method, CookieUtils.getMenuId(request), response);
    }



    public boolean checkAccountHasPathPermission(String account, String servletPath, String method, Long menuId, HttpServletResponse response){
        if (open == null){open = true;}
        if (open && !canVisit(servletPath, systemPath)) {
            if(commonFunctionCheck.check(method,servletPath)){
                return true;
            }
            if (!loginService.checkUserMenuOrFunction(account, servletPath, method, null, menuId)) {
                log.info("账号：" + account + "没有菜单功能权限");
                print(response, Response.error(MSG.permission_menu_error), HttpStatus.FORBIDDEN.value());
                return false;
            }
        }
        return true;
    }

    private boolean canVisit(String path, List<String> paths) {
        boolean flag = false;
        if (StringUtils.isEmpty(path)) {
            return flag;
        }
        for (String s : paths) {
            if (StringUtils.isNotEmpty(pre)) {
                s = pre + s;
            }
            if (s.endsWith("*")) {
                s = s.substring(0, s.lastIndexOf("*"));
                if (path.startsWith(s)) {
                    flag = true;
                    return flag;
                }
            } else if (path.equals(s)) {
                flag = true;
                return flag;
            }
        }
        return flag;
    }
}
